﻿scene.mesh.spring = (function ($, undefined)
{
    var _stiffness = 0.0015;/*0.0005;*/
    var _dampen = 0.99;/*0.998;*/
    
    var _renderFunctions = null;

    var init = function (renderFunctions)
    {
        _renderFunctions = renderFunctions;
    };
    
    var destroy = function ()
    {
        _renderFunctions = null;
    };

    var createSpringsBetweenMeshes = function (meshes, actualDistances)
    {
        // Create a spring between all the meshes
        $.each(meshes, function (i, meshA) {
            meshA.springs = [];
            $.each(meshes, function (j, meshB) {
                if (meshA.identifier === meshB.identifier) return;

                var spring = _createSpring(meshA, meshB, actualDistances);
                meshA.springs.push(spring);
            });
        });

        // Define the calculation of the force, velocity and position of each spring
        $.each(meshes, function (i, mesh) {
            _renderFunctions.push(function () {
                var force = new THREE.Vector3(0, 0, 0);

                $.each(mesh.springs, function (j, spring) {
                    force.add(spring.force());
                });

                mesh.velocity.add(force).multiplyScalar(_dampen);
                mesh.position.add(mesh.velocity);
            });
        });
    };

    var _createSpring = function (meshA, meshB, actualDistances)
    {
        var springLength = _getSpringLength(meshA.identifier, meshB.identifier, actualDistances);
        
        return new _spring(meshA, meshB, springLength);
    };

    var _spring = function(meshA, meshB, springLength)
    {
        this.start = meshA;
        this.end = meshB;
        this.length = springLength;
        this.force = function()
        {
            var deltaVector = new THREE.Vector3(
                this.start.position.x - this.end.position.x,
                this.start.position.y - this.end.position.y,
                this.start.position.z - this.end.position.z);

            var stretch = this.length - deltaVector.length();

            // The force is positive when the spring is pressed in, and negative when the spring is stretched out.
            return deltaVector.normalize().multiplyScalar(0.5 * _stiffness * stretch);
        };
    };

    var _getSpringLength = function (identifierA, identifierB, actualDistances)
    {
        var distances = $.grep(actualDistances,
            function(e) {
                return (e.EntityId == identifierA || e.EntityId == identifierB);
            });

        var distance = $.grep(distances,
            function(d) {
                return $.grep(d.Values,
                    function(e) {
                        return (e.EntityId == identifierA || e.EntityId == identifierB);
                    }).length > 0;
            });

        if (distance[0].EntityId === identifierA) {
            return $.grep(distance[0].Values,
                function(e) {
                    return e.EntityId == identifierB;
                })[0].Value;
        }
        else {
            return $.grep(distance[0].Values,
                function (e) {
                    return e.EntityId == identifierA;
                })[0].Value;
        }
    };
    
    // public API
    return {
        init: init,
        destroy: destroy,
        createSpringsBetweenMeshes: createSpringsBetweenMeshes
    };
})(jQuery);